home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Abalone 1.4.2 / src / Interface.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-21  |  21.1 KB  |  1,128 lines  |  [TEXT/MPS ]

  1. #define INTERFACE_C
  2. #include "Interface.h"
  3. #undef INTERFACE_C
  4.  
  5.  
  6. #if defined(__MWERKS__)
  7. #pragma segment __%Main
  8. #else
  9. #pragma segment Main
  10. #endif
  11.  
  12.  
  13. static char        gMoveNotation[8] = " ";
  14.  
  15.  
  16.  
  17. void
  18. UndoLastMove (void)
  19. {
  20.     (void) PopBoard();
  21.     (void) PopBoard();
  22.     if (gSet.Players == 3)
  23.         (void) PopBoard();
  24.     
  25.     gTheGame.CurrentMove -= gSet.Players;
  26.  
  27.     InvalBoard();
  28. }
  29.  
  30.  
  31.  
  32. Rect 
  33. FieldToRect (Field f)
  34. {
  35.     Point coords = FieldToCarthCoord (f);
  36.     Rect bounds;
  37.     
  38.     bounds.top = coords.v * gSet.FieldHeight;
  39.     bounds.bottom = (coords.v + 1) * gSet.FieldHeight;
  40.     bounds.left = coords.h * gSet.FieldWidth / 2;
  41.     bounds.right = (coords.h + 2) * gSet.FieldWidth / 2;
  42.     
  43.     OffsetRect (& bounds, 1, 0);    //    correction for pixel outside hexagon
  44.     
  45.     return bounds;
  46. }
  47.  
  48.  
  49.  
  50. Point
  51. FieldToCarthCoord (Field f)
  52. {
  53.     Point coords;
  54.         
  55.     if (f > 0 && f <= 5)    { coords.h = 2 * (f -  1) + 4; coords.v = 0; }
  56.     else if (f <= 11)        { coords.h = 2 * (f -  6) + 3, coords.v = 1; }
  57.     else if (f <= 18)        { coords.h = 2 * (f - 12) + 2, coords.v = 2; }
  58.     else if (f <= 26)        { coords.h = 2 * (f - 19) + 1, coords.v = 3; }
  59.     else if (f <= 35)        { coords.h = 2 * (f - 27) + 0, coords.v = 4; }
  60.     else if (f <= 43)        { coords.h = 2 * (f - 36) + 1, coords.v = 5; }
  61.     else if (f <= 50)        { coords.h = 2 * (f - 44) + 2, coords.v = 6; }
  62.     else if (f <= 56)        { coords.h = 2 * (f - 51) + 3, coords.v = 7; }
  63.     else if (f <= 61)        { coords.h = 2 * (f - 57) + 4, coords.v = 8; }
  64.     
  65.     return coords;
  66. }
  67.  
  68.  
  69.  
  70. Point
  71. FieldToLoc (Field f)
  72. {
  73.     Point loc;
  74.     
  75.     loc.h = loc.v = 0;
  76.     
  77.     if (f > 0 && f <= 5)
  78.     {
  79.         loc.h = (f - 1) * gSet.FieldWidth + 5 * gSet.FieldWidth / 2;
  80.         loc.v = 1 * gSet.FieldHeight / 2;
  81.     }
  82.     else if (f <= 11)
  83.     {
  84.         loc.h = (f - 6) * gSet.FieldWidth + 2 * gSet.FieldWidth;
  85.         loc.v = 3 * gSet.FieldHeight / 2;
  86.     }
  87.     else if (f <= 18)
  88.     {
  89.         loc.h = (f - 12) * gSet.FieldWidth + 3 * gSet.FieldWidth / 2;
  90.         loc.v = 5 * gSet.FieldHeight / 2;
  91.     }
  92.     else if (f <= 26)
  93.     {
  94.         loc.h = (f - 19) * gSet.FieldWidth + 1 * gSet.FieldWidth;
  95.         loc.v = 7 * gSet.FieldHeight / 2;
  96.     }
  97.     else if (f <= 35)
  98.     {
  99.         loc.h = (f - 27) * gSet.FieldWidth + 1 * gSet.FieldWidth / 2;
  100.         loc.v = 9 * gSet.FieldHeight / 2;
  101.     }
  102.     else if (f <= 43)
  103.     {
  104.         loc.h = (f - 36) * gSet.FieldWidth + 1 * gSet.FieldWidth ;
  105.         loc.v = 11 * gSet.FieldHeight / 2;
  106.     }
  107.     else if (f <= 50)
  108.     {
  109.         loc.h = (f - 44) * gSet.FieldWidth + 3 * gSet.FieldWidth / 2;
  110.         loc.v = 13 * gSet.FieldHeight / 2;
  111.     }
  112.     else if (f <= 56)
  113.     {
  114.         loc.h = (f - 51) * gSet.FieldWidth + 2 * gSet.FieldWidth;
  115.         loc.v = 15 * gSet.FieldHeight / 2;
  116.     }
  117.     else if (f <= 61)
  118.     {
  119.         loc.h = (f - 57) * gSet.FieldWidth + 5 * gSet.FieldWidth / 2;
  120.         loc.v = 17 * gSet.FieldHeight / 2;
  121.     }
  122.     loc.h++;    //    correction for pixel outside hexagon
  123.     return loc;
  124. }
  125.  
  126.  
  127.  
  128. Rect
  129. FieldToBallRect (Field f)
  130. {
  131.     Point coords = FieldToCarthCoord (f);
  132.     Rect rect;
  133.     short diff = gSet.FieldWidth - gSet.FieldHeight;
  134.  
  135.     rect.top = coords.v * gSet.FieldHeight;
  136.     rect.bottom = rect.top + gSet.FieldHeight + 1;
  137.     rect.left = (coords.h * gSet.FieldWidth)/ 2 + (diff + 1) / 2;
  138.     rect.right = rect.left + gSet.FieldHeight + 1;
  139.     
  140. //    rect.left--;
  141. //    rect.right--;
  142.     
  143.     return rect;
  144. }
  145.  
  146.  
  147.  
  148. void
  149. InvalBoard (void)
  150. {
  151.     Rect boardRect;
  152.     
  153.     if (ColorQDAvailable())
  154.     {
  155.         Update3D();
  156.         RestoreColors();
  157.     }
  158.     
  159.     SetRect (& boardRect, 0, 0, 9 * gSet.FieldWidth + 1 + 1, 9 * gSet.FieldHeight + 1);
  160.     if (gBlackAndWhite)
  161.     {
  162.         PenPat ((ConstPatternParam) & qd.ltGray);
  163.         PaintRect (& boardRect);
  164.         PenPat ((ConstPatternParam) & qd.black);
  165.     }
  166.     else
  167.     {
  168.         EraseRect (& boardRect);
  169.     }
  170.     InvalRect (& boardRect);
  171. }
  172.  
  173.  
  174.  
  175. void
  176. UpdateBoard (WindowPtr window)
  177. {
  178.     SetPort (window);
  179.  
  180.     if (ColorQDAvailable())
  181.     {
  182.         Update3D();
  183.         RestoreColors();
  184.     }
  185.     
  186.     BeginUpdate (window);
  187.     
  188.     if (! EmptyRgn (window->visRgn))    // draw if updating needs to be done
  189.     {
  190.         DrawBackground (window);
  191.         DrawBoard ((BoardPtr) GetWRefCon (window));
  192.         UpdtDialog (window, window->visRgn);
  193.     }
  194.         
  195.     EndUpdate (window);
  196. }
  197.  
  198.  
  199.  
  200. void
  201. DrawBoard (BoardPtr board)
  202. {
  203.     Field f;
  204.     Rect fieldRect;
  205.     WindowPtr window = gAbaloneWindow;
  206.  
  207. //    draw invalidated field backgrounds
  208.  
  209.     for (f = 1; f <= kFields; f++)
  210.     {
  211.     //    if (board[0].field[f] == empty)
  212.         {
  213.         //    fieldRect = FieldToRect (f);
  214.             fieldRect = FieldToBallRect (f);    // Optimization??
  215.             
  216.             if (RectInRgn (& fieldRect, window->visRgn))
  217.                 DrawField (f);
  218.         }
  219.     }
  220.     
  221. //    draw invalidated fields grid lines (if we want them)
  222.  
  223.     if (! gSet.Balls3D)
  224.     {
  225.         for (f = 1; f <= kFields; f++)
  226.         {
  227.             fieldRect = FieldToRect (f);
  228.             
  229.             if (RectInRgn (& fieldRect, window->visRgn))
  230.                 DrawGrid (f);
  231.         }
  232.     }
  233. //    draw invalidated balls
  234. //    this is done separately from the fields,
  235. //    so the balls can overlap the bounds of all fields.
  236.     
  237.     for (f = 1; f <= kFields; f++)
  238.     {
  239.         if (board[0].field[f] != empty)
  240.         {
  241.             Rect ballRect;
  242.         
  243.             ballRect = FieldToBallRect (f);
  244.             
  245.             if (RectInRgn (& ballRect, window->visRgn))
  246.                 DrawBall (f, board[0].field[f]);
  247.         }
  248.     }      
  249. }
  250.  
  251.  
  252.  
  253. void
  254. InvalItems (WindowPtr wp)
  255. {
  256.     short    type, item;
  257.     Handle    hndl;
  258.     Rect    box;
  259.     
  260.     Assert (wp == gAbaloneWindow, ILLEGAL_PARAMETER);
  261.  
  262.     SetPort (wp);
  263.     for (item = 1; item <= 4; item++)
  264.     {
  265.         GetDItem (wp, item, & type, & hndl, & box);
  266.         InvalRect (& box);
  267.     }
  268.     if (gBlackAndWhite)
  269.     {
  270.         PenPat ((ConstPatternParam) & qd.ltGray);
  271.         PaintRect (& box);    //    Erase notation item
  272.         PenPat ((ConstPatternParam) & qd.black);
  273.     }
  274.     else
  275.     {
  276.         EraseRect (& box);    //    Erase notation item
  277.     }
  278. }
  279.  
  280.  
  281.  
  282. void
  283. SizeItems (WindowPtr wp)
  284. {
  285.     short    type;
  286.     Handle    hndl;
  287.     Rect    box;
  288.  
  289.     Assert (wp == gAbaloneWindow, ILLEGAL_PARAMETER);
  290.  
  291.     SetPort (wp);
  292.  
  293. //    resize and move item 1 to fit in bottom left corner
  294.  
  295.     GetDItem (wp, 1, & type, & hndl, & box);
  296.     InvalRect (& box);
  297.     
  298.     box = wp->portRect;
  299.     box.left += 2;
  300.     box.bottom -= 2;
  301.     box.top = box.bottom - 3 * gSet.FieldHeight / 2;
  302.     box.right = box.left + 3 * gSet.FieldHeight / 2;
  303.  
  304.     SetDItem (wp, 1, type, hndl, & box);
  305.     InvalRect (& box);
  306.  
  307. //    resize and move item 2 to fit in bottom right corner
  308.     
  309.     GetDItem (wp, 2, & type, & hndl, & box);
  310.     InvalRect (& box);
  311.     
  312.     box = wp->portRect;
  313.     box.right -= 2;
  314.     box.bottom -= 2;
  315.     box.top = box.bottom - 3 * gSet.FieldHeight / 2;
  316.     box.left = box.right - 3 * gSet.FieldHeight / 2;
  317.  
  318.     SetDItem (wp, 2, type, hndl, & box);
  319.     InvalRect (& box);
  320.  
  321. //    resize and move item 3 to fit in top right corner
  322.     
  323.     GetDItem (wp, 3, & type, & hndl, & box);
  324.     InvalRect (& box);
  325.     
  326.     box = wp->portRect;
  327.     box.right -= 2;    
  328.     box.top += 2;
  329.     box.bottom = box.top + 3 * gSet.FieldHeight / 2;
  330.     box.left = box.right - 3 * gSet.FieldHeight / 2;
  331.  
  332.     SetDItem (wp, 3, type, hndl, & box);
  333.     InvalRect (& box);
  334. }
  335.  
  336.  
  337.  
  338. void
  339. DrawPoint (Rect *box, short color)
  340. {
  341.     if (! gBlackAndWhite)
  342.     {
  343.         RGBForeColor (& gSet.Color[color]);
  344.         PaintOval (box);
  345.         ForeColor (blackColor);
  346.         return;
  347.     }
  348.     switch (color)
  349.     {
  350.         case whit:
  351.             EraseOval (box);
  352.         break;
  353.         case blak:
  354.             PaintOval (box);
  355.         break;
  356.         case grin:
  357.             PenPat ((ConstPatternParam) & qd.gray);
  358.             PaintOval (box);
  359.             PenNormal();
  360.         break;
  361.     }
  362. }
  363.  
  364.  
  365. //    Draw a small ball in the bottom left corner of the screen for each ball black has won.
  366.  
  367. pascal void
  368. DrawBlackPoints (WindowPtr wp, short item)
  369. {
  370.     BoardPtr    board = (BoardPtr) GetWRefCon (gAbaloneWindow);
  371.     short        points = BallsWon (board, blak);
  372.         
  373.     if (points != 0)
  374.     {        
  375.         short    type;
  376.         Handle    hndl;
  377.         Point    oldLoc;
  378.         Rect    box;
  379.         short    ball;
  380.     
  381.         GrafPtr    oldPort;
  382.         GetPort (& oldPort);
  383.         GetPen (& oldLoc);
  384.         SetPort (wp);
  385.     
  386.         GetDItem (wp, item, & type, & hndl, & box);
  387.         
  388.         box.top = box.bottom - gSet.FieldHeight/2;
  389.         box.right = box.left + gSet.FieldHeight/2;
  390.         
  391.         for (ball = 1; ball <= points; ball++)
  392.         {
  393.             DrawPoint (& box, board->loot[blak-1][ball]);
  394.             
  395.         //    Compute the position for the next ball such that the balls are stacked neatly.
  396.             
  397.             switch (ball)
  398.             {
  399.                 case 1:
  400.                 case 2:
  401.                 case 4:
  402.                     box.left    += gSet.FieldHeight / 2;
  403.                     box.right    += gSet.FieldHeight / 2;
  404.                 break;
  405.                 case 3:
  406.                     box.left    -= gSet.FieldHeight / 2;
  407.                     box.right    -= gSet.FieldHeight / 2;
  408.                 case 5:
  409.                     box.left    -= gSet.FieldHeight / 4;
  410.                     box.right    -= gSet.FieldHeight / 4;
  411.                     box.top        -= ((short) (1.74 * gSet.FieldHeight)) / 4;
  412.                     box.bottom    -= ((short) (1.74 * gSet.FieldHeight)) / 4;
  413.                 break;
  414.             }
  415.         }
  416.         MoveTo (oldLoc.h, oldLoc.v);
  417.         SetPort (oldPort);
  418.     }
  419. }
  420.  
  421.  
  422. //    Draw a small ball in the bottom right corner of the screen for each ball white has won.
  423.  
  424. pascal void
  425. DrawWhitePoints (WindowPtr wp, short item)
  426. {
  427.     BoardPtr    board = (BoardPtr) GetWRefCon (gAbaloneWindow);
  428.     short        points = BallsWon (board, whit);
  429.     
  430.     if (points != 0)
  431.     {
  432.         short    type;
  433.         Handle    hndl;
  434.         Point    oldLoc;
  435.         Rect    box;
  436.         short    ball;
  437.     
  438.         GrafPtr    oldPort;
  439.         GetPort (& oldPort);
  440.         GetPen (& oldLoc);
  441.         SetPort (wp);
  442.     
  443.         GetDItem (wp, item, & type, & hndl, & box);
  444.         
  445.         box.top = box.bottom - gSet.FieldHeight/2;
  446.         box.left = box.right - gSet.FieldHeight/2;
  447.         
  448.         for (ball = 1; ball <= points; ball++)
  449.         {
  450.             DrawPoint (& box, board->loot[whit-1][ball]);
  451.  
  452.             switch (ball)
  453.             {
  454.                 case 1:
  455.                 case 2:
  456.                 case 4:
  457.                     box.left    -= gSet.FieldHeight / 2;
  458.                     box.right    -= gSet.FieldHeight / 2;
  459.                 break;
  460.                 case 3:
  461.                     box.left    += gSet.FieldHeight / 2;
  462.                     box.right    += gSet.FieldHeight / 2;
  463.                 case 5:
  464.                     box.left    += gSet.FieldHeight / 4;
  465.                     box.right    += gSet.FieldHeight / 4;
  466.                     box.top        -= ((short) (1.74 * gSet.FieldHeight)) / 4;
  467.                     box.bottom    -= ((short) (1.74 * gSet.FieldHeight)) / 4;
  468.                 break;
  469.             }
  470.         }
  471.         MoveTo (oldLoc.h, oldLoc.v);
  472.         SetPort (oldPort);
  473.     }
  474. }
  475.  
  476.  
  477.  
  478. //    Draw a small ball in the top right corner of the screen for each ball green has won.
  479.  
  480. pascal void
  481. DrawGreenPoints (WindowPtr wp, short item)
  482. {
  483.     BoardPtr    board = (BoardPtr) GetWRefCon (gAbaloneWindow);
  484.     short        points = BallsWon (board, grin);
  485.     
  486.     if (points != 0)
  487.     {
  488.         short    type;
  489.         Handle    hndl;
  490.         Point    oldLoc;
  491.         Rect    box;
  492.         short    ball;
  493.     
  494.         GrafPtr    oldPort;
  495.         GetPort (& oldPort);
  496.         GetPen (& oldLoc);
  497.         SetPort (wp);
  498.     
  499.         GetDItem (wp, item, & type, & hndl, & box);
  500.         
  501.         box.bottom = box.top + gSet.FieldHeight/2;
  502.         box.left = box.right - gSet.FieldHeight/2;
  503.  
  504.         for (ball = 1; ball <= points; ball++)
  505.         {
  506.             DrawPoint (& box, board->loot[grin-1][ball]);
  507.  
  508.             switch (ball)
  509.             {
  510.                 case 1:
  511.                 case 2:
  512.                 case 4:
  513.                     box.left    -= gSet.FieldHeight / 2;
  514.                     box.right    -= gSet.FieldHeight / 2;
  515.                 break;
  516.                 case 3:
  517.                     box.left    += gSet.FieldHeight / 2;
  518.                     box.right    += gSet.FieldHeight / 2;
  519.                 case 5:
  520.                     box.left    += gSet.FieldHeight / 4;
  521.                     box.right    += gSet.FieldHeight / 4;
  522.                     box.top        += ((short) (1.74 * gSet.FieldHeight)) / 4;
  523.                     box.bottom    += ((short) (1.74 * gSet.FieldHeight)) / 4;
  524.                 break;
  525.             }
  526.         }
  527.         MoveTo (oldLoc.h, oldLoc.v);
  528.         SetPort (oldPort);
  529.     }
  530. }
  531.  
  532.  
  533.  
  534. void
  535. EraseBall (Field field)
  536. {
  537.     Rect ballRect;
  538.         
  539.      ballRect = FieldToBallRect (field);
  540.             
  541.     if (gBlackAndWhite)
  542.     {
  543.         PenPat ((ConstPatternParam) & qd.ltGray);
  544.         PaintOval (& ballRect);
  545.         PenPat ((ConstPatternParam) & qd.black);
  546.     }
  547.     else
  548.     {
  549.         EraseOval (& ballRect);
  550.     }
  551. }
  552.  
  553.  
  554.  
  555. void
  556. EraseField (Field field)
  557. {
  558.     Rect fieldRect;
  559.     
  560.     fieldRect = FieldToRect (field);
  561.     EraseRect (& fieldRect);
  562. }
  563.  
  564.  
  565.  
  566. short
  567. PixelSize (void)
  568. {
  569.     return (*(((CWindowPtr) gAbaloneWindow)->portPixMap))->pixelSize;
  570. }
  571.  
  572.  
  573. void
  574. DrawField (Field field)
  575. {
  576.     Rect    ballRect;
  577.     
  578.     if (! field)
  579.         return;
  580.         
  581.     if (! gBlackAndWhite || gSet.Balls3D)    // not the most simple case
  582.     {
  583.         DrawFieldColor (field);
  584.         return;
  585.     }
  586.     EraseBall (field);
  587.     ballRect = FieldToBallRect (field);
  588.     ForeColor (blackColor);
  589.     InsetRect (& ballRect, gSet.FieldHeight / 3, gSet.FieldHeight / 3);
  590.     PaintOval (& ballRect);
  591. }
  592.  
  593.  
  594.  
  595. void
  596. DrawFieldColor (Field field)
  597. {
  598.     Rect         ballRect;
  599.     RGBColor    fieldColor;
  600.         
  601. //    Draw the ball in color
  602.     
  603.     if (gSet.Balls3D)    // The real hard case
  604.     {
  605.         DrawFieldColor3D (field);
  606.         return;
  607.     }
  608.     EraseField (field);
  609.     ballRect = FieldToBallRect (field);
  610.     fieldColor = gSet.Color[0];
  611.     Darken (& fieldColor);
  612.     RGBForeColor (& fieldColor);
  613.     InsetRect (& ballRect, gSet.FieldHeight / 3, gSet.FieldHeight / 3);
  614.     PaintOval (& ballRect);
  615.     RestoreColors();
  616. }
  617.  
  618.  
  619.  
  620. void
  621. DrawGrid (Field field)
  622. {
  623.     Rect fieldRect;
  624.     
  625.     if (! field)
  626.         return;
  627.         
  628.     if (! gBlackAndWhite)    // not the most simple case
  629.     {
  630.         DrawGridColor (field);
  631.         return;
  632.     }
  633.     fieldRect = FieldToRect (field);
  634.  
  635.     FrameHexagon (& fieldRect);
  636. }
  637.  
  638.  
  639.  
  640. void
  641. DrawGridColor (Field field)
  642. {
  643.     Rect        fieldRect = FieldToRect (field);
  644.     RGBColor    fieldColor = gSet.Color[0];
  645.     
  646.     Darken (& fieldColor);
  647.     Darken (& fieldColor);
  648.     RGBForeColor (& fieldColor);
  649.     FrameHexagon (& fieldRect);
  650.     RestoreColors();
  651. }
  652.  
  653.  
  654.  
  655.  
  656. void
  657. DrawBall (Field field, char color)
  658. {
  659.     Rect         ballRect;
  660.     
  661.     if (! field || ! color)
  662.         return;
  663.     
  664.     if (! gBlackAndWhite || gSet.Balls3D)    // not the most simple case
  665.     {
  666.         DrawBallColor (field, color);
  667.         return;
  668.     }
  669.         
  670.      ballRect = FieldToBallRect (field);
  671.  
  672. //    Simple case: PaintOval the ball, done.
  673.     
  674.     switch (color)
  675.     {
  676.         case whit:
  677.             EraseOval (& ballRect);
  678.         break;
  679.         default:
  680.         case blak:
  681.             PaintOval (& ballRect);
  682.         break;
  683.         case grin:    //    gray, on this screen
  684.             PenPat ((ConstPatternParam) & qd.gray);
  685.             PaintOval (& ballRect);
  686.             PenNormal();
  687.         break;
  688.     }
  689. }
  690.  
  691.  
  692.  
  693. void
  694. DrawBallColor (Field field, char color)
  695. {
  696.     Rect         ballRect;
  697.         
  698. //    Draw the ball in color
  699.     
  700.     if (gSet.Balls3D)    // The real hard case
  701.     {
  702.         DrawBallColor3D (field, color);
  703.         return;
  704.     }
  705. //    Simple case: PaintOval the ball in the right color
  706.  
  707.      ballRect = FieldToBallRect (field);
  708.  
  709.     RGBForeColor (& gSet.Color[color]);
  710.     PaintOval (& ballRect);
  711.     ForeColor (blackColor);
  712. }
  713.  
  714.  
  715.  
  716. void
  717. FrameHexagon (Rect * r)
  718. {
  719.     short    hor = r->right - r->left,
  720.             ver = r->bottom - r->top,
  721.             ddH = (hor + 1) / 2,
  722.             ddV = ver,
  723.             dH =  (hor + 2) / 4,
  724.             dV =  (ver + 1) / 2;
  725.  
  726.     MoveTo (r->left + dH - 1, r->top);
  727.     Line (ddH, 0);
  728.     Line (dH, dV);
  729.     Line (-dH, ddV - dV);
  730.     Line (-ddH, 0);
  731.     Line (-dH, dV - ddV);
  732.     Line (dH, -dV);
  733. }
  734.  
  735.  
  736.  
  737. void
  738. DoFieldClick (Field f)
  739. {
  740.     BoardPtr    board = (BoardPtr) GetWRefCon (gAbaloneWindow);
  741.     
  742.     if (    gSet.PlayerKind[gTheGame.CurrentPlayer] != humanPlayer
  743.         ||    board->field[f] != gTheGame.CurrentPlayer)
  744.     {
  745.         SndPlayBeep();
  746.     }
  747.     else
  748.     {
  749.         Point        mouseLoc;
  750.         long        position;
  751.         Rect        ballRect, fieldRect, limitRect, slopRect;
  752.         RgnHandle    ballRgn = NewRgn();
  753.         Field        toField;
  754.         MoveData    move;
  755.         
  756.         ballRect = FieldToBallRect (f);
  757.         fieldRect = FieldToRect (f);
  758.         
  759.         OpenRgn();
  760.             FrameOval (& ballRect);
  761.         CloseRgn (ballRgn);
  762.         
  763.         limitRect = slopRect = fieldRect;
  764.         InsetRect (& limitRect, -gSet.FieldWidth/2 - 1, -gSet.FieldHeight/2 - 1);
  765.         InsetRect (& slopRect, -gSet.FieldWidth, -gSet.FieldHeight - 1);
  766.         mouseLoc = FieldToLoc (f);
  767.         position = DragGrayRgn (ballRgn, mouseLoc, & limitRect, & slopRect, noConstraint, NULL);
  768.         
  769.         DisposeRgn (ballRgn), ballRgn = 0;
  770.         
  771.         mouseLoc.v += ((position) >> 16) & 0xFFFF;
  772.         mouseLoc.h += position & 0xFFFF;
  773.     
  774.         toField = LocToField (mouseLoc.h, mouseLoc.v);
  775.         move[0] = f;
  776.         move[1] = move[2] = 0;
  777.         move[3] = Direction (f, toField);
  778.         
  779.         if (board->field [f] == gTheGame.CurrentPlayer)
  780.         {
  781.             if (ProcessMove (move, false))
  782.             {
  783.             //    The broadcast is done after the board has been modified:
  784.             //    The move is still valid, so we can first send just the move,
  785.             //    but if sending the move somehow fails,
  786.             //    The whole board is already updated and can be sent 'as is'.
  787.                 ;
  788.                 BroadcastMove (move, PriorPlayer (CurrentPlayer()), CheckSum (RecentBoard()));
  789.             }
  790.         }        
  791.     }
  792. }
  793.  
  794.  
  795.  
  796. void
  797. DoFieldShiftClick (Field f1)
  798. {
  799.     BoardPtr    board = (BoardPtr) GetWRefCon (gAbaloneWindow);
  800.     
  801.     if (    gSet.PlayerKind[gTheGame.CurrentPlayer] != humanPlayer
  802.         ||    board->field[f1] != gTheGame.CurrentPlayer)
  803.     {
  804.         SndPlayBeep();
  805.     }
  806.     else
  807.     {
  808.         Point        mouseLoc;
  809.         long        position;
  810.         Rect        ballRect, fieldRect, limitRect, slopRect;
  811.         MoveData    f;
  812.         Field        downField, upField;
  813.         short        b;
  814.         RgnHandle     ballRgn;
  815.         
  816.         f[0] = f[1] = f[2] = downField = upField = 0;
  817.     
  818.     
  819.         for (b = 0; b < 4; b++)
  820.         {
  821.             EventRecord nextEvent;
  822.                     
  823.             downField = f1;    //    for the first ball, use the original mouse-down
  824.         
  825.             if (b != 0)        //    for all other balls, get a new mouse-down
  826.             {
  827.                 do
  828.                      GetNextEvent (everyEvent, & nextEvent);
  829.                 while (nextEvent.what != mouseDown);
  830.                 
  831.                 GlobalToLocal (& nextEvent.where);
  832.                 downField = LocToField (nextEvent.where.h, nextEvent.where.v);
  833.             }
  834.             
  835.             fieldRect = FieldToRect (downField);
  836.             ballRect = FieldToBallRect (downField);
  837.     
  838.             if (board->field[downField] == empty)    //    cancel the selection & get out of here
  839.             {
  840.                 short b2;
  841.                 
  842.                 for (b2 = 0; b2 < b; b2++)            //    deselect all balls
  843.                 
  844.                     XORBall (f[b2]);
  845.     
  846.                 return;
  847.             }
  848.             if (    b > 0 && downField == f[b-1]    //    mouse-down in the same ball twice:
  849.                 ||    b > 1 && downField == f[b-2]    //    assume user is starting to drag
  850.                 ||    b > 2 && downField == f[b-3]
  851.                 )
  852.                 break;
  853.             
  854.             if (b == 3)
  855.                 break;
  856.             
  857.             f[b] = downField;                        //    add ball to selection    
  858.             
  859.             if (! ValidFliche (board, f))
  860.             {
  861.                 f[b] = 0;
  862.                 --b;
  863.                 SndPlayBeep();
  864.                 continue;
  865.             }
  866.                 
  867.             XORBall (f[b]);                            //    show the ball is selected
  868.     
  869.             do    //    Wait for mouse-up or mouse out of current field
  870.             {
  871.                 GetMouse (& mouseLoc);
  872.                 (void) GetNextEvent (everyEvent, & nextEvent);
  873.             }
  874.             while (nextEvent.what != mouseUp && PtInRect (mouseLoc, & fieldRect));
  875.     
  876.             GlobalToLocal (& nextEvent.where);
  877.             upField = LocToField (nextEvent.where.h, nextEvent.where.v);
  878.             if (! PtInRect (mouseLoc, & fieldRect))
  879.                 break;
  880.         }
  881.         
  882.         ballRgn = NewRgn();
  883.         OpenRgn();
  884.         for (b = 0; b < 3; b++)
  885.         {
  886.             if (f[b] != 0)
  887.             {
  888.                 ballRect = FieldToBallRect (f[b]);
  889.                 FrameOval (& ballRect);
  890.             }
  891.         }
  892.         CloseRgn (ballRgn);
  893.         
  894.         limitRect = slopRect = fieldRect;
  895.         InsetRect (& limitRect, -gSet.FieldWidth/2, -gSet.FieldHeight/2);
  896.         InsetRect (& slopRect, -gSet.FieldWidth, -gSet.FieldHeight);
  897.         mouseLoc = FieldToLoc (downField);
  898.     
  899.         for (b = 0; b < 3; b++)
  900.         
  901.             if (f[b] != 0)
  902.             
  903.                 XORBall (f[b]);
  904.                 
  905.         position = DragGrayRgn (ballRgn, mouseLoc, & limitRect, & slopRect, noConstraint, NULL);
  906.         DisposeRgn (ballRgn), ballRgn = 0;
  907.         
  908.         mouseLoc.v += ((position) >> 16) & 0xFFFF;
  909.         mouseLoc.h += position & 0xFFFF;
  910.     
  911.         upField = LocToField (mouseLoc.h, mouseLoc.v);
  912.         f[3] = Direction (downField, upField);
  913.         
  914.         if    (board->field [f[0]] == gTheGame.CurrentPlayer)
  915.         {
  916.             if (ProcessMove (f, false))
  917.             {
  918.             //    The broadcast is done after the board has been modified:
  919.             //    The move is still valid, so we can first send just the move,
  920.             //    but if sending the move somehow fails,
  921.             //    The whole board is already updated and can be sent 'as is'.
  922.                 
  923.                 BroadcastMove (f, PriorPlayer (CurrentPlayer()), CheckSum (RecentBoard()));
  924.             }
  925.         }        
  926.     }
  927. }
  928.  
  929.  
  930.  
  931. void
  932. InvalBall (Field field)
  933. {
  934. //    Rect ballRect = FieldToBallRect (field);
  935. //    
  936. //     SetPort (FrontWindow());
  937. //     InvalRect (& ballRect);
  938.  
  939.  
  940.     Rect ballRect = FieldToBallRect (field);
  941.      RgnHandle ballRgn = NewRgn();
  942.      
  943.      SetPort (FrontWindow());
  944.      OpenRgn();
  945.      FrameOval (& ballRect);
  946.      CloseRgn (ballRgn);
  947.      InvalRgn (ballRgn);
  948.      DisposeRgn (ballRgn);
  949. }
  950.  
  951.  
  952.  
  953. void
  954. ValidBall (Field field)
  955. {
  956.     Rect ballRect = FieldToBallRect (field);
  957.      RgnHandle ballRgn = NewRgn();
  958.      
  959.      SetPort (FrontWindow());
  960.      OpenRgn();
  961.      FrameOval (& ballRect);
  962.      CloseRgn (ballRgn);
  963.      ValidRgn (ballRgn);
  964.      DisposeRgn (ballRgn);
  965. }
  966.  
  967.  
  968.  
  969.  
  970. void
  971. XORBall (Field field)
  972. {
  973.     Rect r = FieldToBallRect (field);
  974.     
  975.     PenPat ((ConstPatternParam) & qd.black);
  976.     PenMode (patXor);
  977.     PenSize (4, 4);
  978.     FrameOval (& r);
  979.     PenNormal();
  980. }
  981.  
  982.  
  983.  
  984. void
  985. ShowMove (MovePtr move, Boolean animate)
  986. {
  987.     ShowPropMove (move[0], move[3], animate);
  988. }
  989.  
  990.  
  991.  
  992. void
  993. ShowPropMove (Field f, short direction, Boolean animate)
  994. {
  995.     if (f == 0 || direction == down || FieldOwner (f) == empty)
  996.     {
  997.         long dummy;
  998.         
  999.         if (f != 0) InvalBall (f);
  1000.         if (animate && ! gSet.SoundOn) Delay (10L, & dummy);
  1001.         
  1002.         return;
  1003.     }
  1004.     InvalBall (f);
  1005.     if (animate) XORBall (f);
  1006.     ShowPropMove (Neighbour (f, direction), direction, animate);
  1007. }
  1008.  
  1009.  
  1010.  
  1011. void
  1012. ShowFlicheMove (MovePtr move, Boolean animate)
  1013. {
  1014.     long dummy;
  1015.     
  1016.     if (move[0]) { InvalBall (move[0]); if (animate) XORBall (move[0]); }
  1017.     if (move[1]) { InvalBall (move[1]); if (animate) XORBall (move[1]); }
  1018.     if (move[2]) { InvalBall (move[2]); if (animate) XORBall (move[2]); }
  1019.     if (move[0]) InvalBall (Neighbour (move[0], move[3]));
  1020.     if (move[1]) InvalBall (Neighbour (move[1], move[3]));
  1021.     if (move[2]) InvalBall (Neighbour (move[2], move[3]));
  1022.     if (animate && ! gSet.SoundOn)
  1023.         Delay (10L, & dummy);
  1024. }
  1025.  
  1026.  
  1027.  
  1028. void
  1029. RestoreColors (void)
  1030. {
  1031.     ForeColor (blackColor);
  1032.     if (! ColorQDAvailable())
  1033.         BackColor (whiteColor);
  1034.     else
  1035.         RGBBackColor (& gSet.Color[0]);
  1036. }
  1037.  
  1038.  
  1039.  
  1040. void
  1041. ShowNotation (MovePtr move, BoardPtr board)
  1042. {
  1043.     short    type;
  1044.     Rect    box;
  1045.     Handle    hndl;
  1046.     GrafPtr    oldPort;
  1047.     
  1048. //    just store the move in case notation is turned on
  1049.     
  1050.     MoveToContestNotation (move, gMoveNotation, board);
  1051.     
  1052.     if (! gSet.ShowNotation)
  1053.         return;
  1054.  
  1055.     GetPort (& oldPort);
  1056.     SetPort (gAbaloneWindow);
  1057.     GetDItem (gAbaloneWindow, 4, & type, & hndl, & box);
  1058.     InvalRect (& box);
  1059.     SetPort (oldPort);
  1060. }
  1061.  
  1062.  
  1063.  
  1064. //    draw the contest notation for a move
  1065.  
  1066. pascal void
  1067. DrawNotationItemProc (WindowPtr wp, short item)
  1068. {
  1069.     short    type;
  1070.     Rect    box;
  1071.     Handle    hndl;
  1072.     GrafPtr    oldPort;
  1073.     
  1074.     if (! gSet.ShowNotation)
  1075.         return;
  1076.  
  1077.     GetPort (& oldPort);
  1078.     SetPort (wp);
  1079.     
  1080.     GetDItem (wp, 4, & type, & hndl, & box);
  1081.     
  1082.     if (! gBlackAndWhite)
  1083.     {
  1084.         RGBColor    itemColor = gSet.Color[0];
  1085.         
  1086.         Darken (& itemColor);
  1087.         Darken (& itemColor);
  1088.         RGBForeColor (& itemColor);
  1089.     }
  1090.     
  1091.     FrameRect (& box);
  1092.     TextFont (monaco);
  1093.     TextFace (bold);
  1094.     InsetRect (& box, 1, 1);
  1095.     TextBox (gMoveNotation, strlen (gMoveNotation), & box, teJustCenter);
  1096.     TextFace (normal);
  1097.     TextFont (systemFont);
  1098.     
  1099.     if (! gBlackAndWhite)
  1100.     {
  1101.         RestoreColors();
  1102.     }
  1103.     
  1104.     SetPort (oldPort);
  1105. }
  1106.  
  1107.  
  1108.  
  1109. void
  1110. Darken (RGBColor *color)
  1111. {
  1112.     color->red   -= (color->red   >> 2);
  1113.     color->green -= (color->green >> 2);
  1114.     color->blue  -= (color->blue  >> 2);
  1115. }
  1116.  
  1117.  
  1118.  
  1119. void
  1120. Lighten (RGBColor *color)
  1121. {
  1122.     color->red   += ((0xffff - color->red)   >> 2);
  1123.     color->green += ((0xffff - color->green) >> 2);
  1124.     color->blue  += ((0xffff - color->blue)  >> 2);
  1125. }
  1126.  
  1127.  
  1128.